/*******************************************************************************
 * Copyright 2010 DemandSpot
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.demandspot.utils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;


/**
 * Common URL manipulation utilities
 * @author tal
 *
 */

public class UrlUtils {
	/**
	 * 
	 */
	private static final Pattern URL_PATTERN = Pattern.compile("(?<!(?:href[=]['\"]?|\\>\\s{0,140}))\\b(?:(?:ht|f)tps?[:]//|(?<![:]//)www\\.)[\\S]*\\b", Pattern.CASE_INSENSITIVE);
	private static final Pattern URL_PROTOCOL_PATTERN = Pattern.compile("\\b(?:(?:ht|f)tps?[:]//[\\S]*)\\b", Pattern.CASE_INSENSITIVE);
	private static final String DEFAULT_PROTOCOL = "http://";
	private static final Pattern TWITTER_USER = Pattern.compile("(?<!\\S)@(\\w+)", Pattern.CASE_INSENSITIVE);
	/**
	 * Encode a url using UTF-8
	 * @param string
	 * @return
	 */
	public static String encode(String string) {
		try {
			return URLEncoder.encode(string, "UTF8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("Could not encode the string: " + string);
		}
	}
	
	/**
	 * decode a url using UTF-8
	 * @param string
	 * @return
	 */
	public static String decode(String string) {
		try {
			return URLDecoder.decode(string, "UTF8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("Could not decode the string: " + string);
		}
	}
	
	/**
	 * Get a pretty text of request parameters (good for logging)
	 * @param request
	 * @return
	 */
	public static String getRequestParamString(HttpServletRequest request) {
		Enumeration<String> paramNames = request.getParameterNames();
		if (paramNames != null) {
			StringBuilder sb = new StringBuilder("{");
			String paramName, paramValues[];
			while(paramNames.hasMoreElements()) {
				paramName = paramNames.nextElement();
				paramValues = request.getParameterValues(paramName);
				
				sb.append(paramName + "=" + Arrays.toString(paramValues));
				
				if (!paramNames.hasMoreElements()) 
					return sb.append("}").toString();
				
				sb.append(", ");
			}
		}
		return "{}";
	}
	
	/**
	 * Standard request description (good for logging)
	 * @param request
	 * @return
	 */
	public static String getRequestDescription(ServletRequest request) {
		StringBuilder logEntry = new StringBuilder();
		
		boolean isHttpServletRequest = (request instanceof HttpServletRequest);
		
		if (isHttpServletRequest) {
			HttpServletRequest httpRequest = (HttpServletRequest)request;	
			
			HttpSession session = httpRequest.getSession(false);
			if (session != null) {
				logEntry.append(session.getId());				
			}			
			logEntry.append('\t');
			logEntry.append(httpRequest.getRemoteAddr()).append('\t');			
			logEntry.append(httpRequest.getRequestURL()).append('\t');
			logEntry.append(getRequestParamString(httpRequest));				
		}
		
		return logEntry.toString();
	}

	public static String getParameter(String queryString, String parameterName) {
		parameterName = parameterName + "=";
		if (queryString != null && queryString.length() > 0) {
			int begin = queryString.indexOf(parameterName);
			if (begin != -1) {
				begin += parameterName.length();
				int end = queryString.indexOf("&", begin);
				if (end == -1) {
					end = queryString.length();
				}
				return decode(queryString.substring(begin, end));
			}
		}
		return null;
	}

	public static String getLastRestElement(String queryString) {
		if (queryString != null && queryString.length() > 0) {
			String[] restParts = queryString.split("/");
			for (int j=restParts.length - 1; j >= 0; j--) {
				String restPart = restParts[j];
				if (restPart != null && !restPart.isEmpty()) {
					return restPart;
				}
			}
		}
		return null;
	}
	
	public static Integer getLastIntRestElement(String queryString) {
		if (queryString != null && queryString.length() > 0) {
			String[] restParts = queryString.split("/");
			for (int j=restParts.length - 1; j >= 0; j--) {
				String restPart = restParts[j];
				if (restPart != null && !restPart.isEmpty()) {
					try {
						Integer i = Integer.valueOf(restPart);
						return i;
					} catch (NumberFormatException e) {
					}
				}
			}
		}
		return null;
	}

	
	public static String linkify(String text) {
		//linkify regular links
		Matcher matcher = URL_PATTERN.matcher(text);
		
		StringBuffer sb = new StringBuffer();
		while (matcher.find()) {
			String group = matcher.group();
			boolean addProtocol = !URL_PROTOCOL_PATTERN.matcher(group).matches();
			matcher.appendReplacement(sb, "<a href='" + (addProtocol ? DEFAULT_PROTOCOL : "") + group + "' target='_blank'>" + group + "</a>");
		}
		matcher.appendTail(sb);

		//linkify Twitter user names
		matcher = TWITTER_USER.matcher(sb.toString());		
		sb = new StringBuffer();
		while (matcher.find()) {
			String group = matcher.group();
			String username = group.substring(1);
			matcher.appendReplacement(sb, "<a href='http://twitter.com/" + username + "' target='_blank'>" + group + "</a>");
		}
		matcher.appendTail(sb);
		
		return sb.toString();
	}
	
	public static String linkify(String text, LinkifyCallBack cb) {
		Matcher matcher = URL_PATTERN.matcher(text);
		
		StringBuffer sb = new StringBuffer();
		while (matcher.find()) {
			String group = matcher.group();
			matcher.appendReplacement(sb, cb.getReplacementString(group));
		}
		matcher.appendTail(sb);
		
		return sb.toString();
	}

	public static String getHash(String location) {
		int index = location.indexOf('#'); 
		if (index != -1 && index < location.length()-1) {
			return location.substring(index+1);
		}
		return null;
	}

	public static String replaceHashWithParameter(String location, String paramName) {
		String[] parts = location.split("#");
		if (parts.length == 1) {
			return location;
		}
		if (parts[0].indexOf('?') == -1) {
			return parts[0] + '?' + paramName + "=" + parts[1];
		}
		return parts[0] + '&' + paramName + "=" + parts[1]; 
	}
	
	public static String addProtocolIfNeeded(String url) {
		boolean addProtocol = !URL_PROTOCOL_PATTERN.matcher(url).matches();
		return addProtocol ? DEFAULT_PROTOCOL + url : url;
	}
	
	public static interface LinkifyCallBack {
		String getReplacementString(String url);
	}
	
}
